package com.tmt.helper;

import com.tmt.annotation.parser.LazyLoadParser;
import lombok.extern.slf4j.Slf4j;

import java.lang.annotation.Annotation;
import java.lang.reflect.*;
import java.util.*;
import java.util.stream.Collectors;

/**
 * Created by ThreeManTeam on 2017/5/25.
 */
public class ReflectHelper {

    public static Object invokeMethod(Object objectClass, String methodName, Object[] paramValues, Class[] paramType) {
        try {
            Method method = objectClass.getClass().getMethod(methodName, paramType);
            return method.invoke(objectClass, paramValues);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    public static <T> void invokeMethod(Class<T> objectClass, Object objectInstance, String methodName, Object[] paramValues, Class[] paramType) {
        try {
            Method method = objectClass.getMethod(methodName, paramType);
            method.invoke(objectInstance, paramValues);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public static <T> Object getPropObjectByPropPath(T propObj, String propPath) throws Exception {
        String[] paths = propPath.split("\\.");
        Object obj = propObj;
        for (String path : paths) {
            String[] tmpArr = path.split("\\[");
            String propName = tmpArr[0];
            int index = tmpArr.length > 1 ? Integer.parseInt(tmpArr[1].replace("]", "")) : -1;

            String methodName = "get" + StringHelper.upperFirst(propName);
            String methodName1 = "is" + StringHelper.upperFirst(propName);
            Method getMethod = null;
            try {
                getMethod = obj.getClass().getMethod(methodName, null);
            } catch (Exception ex) {
            }

            try {
                if (getMethod == null)
                    getMethod = obj.getClass().getMethod(methodName1, null);
            } catch (Exception ex) {
            }

            obj = _getTargetValue(obj, getMethod, index, true);
        }
        return obj;
    }

    /**
     * 根据属性路径获得属性对象。
     *
     * @param propObj
     * @param propPath
     * @param <T>
     */
    public static <T> Object getPropObjectByPropPath(T propObj, String propPath, Object defaultValue) throws Exception {
        String[] paths = propPath.split("\\.");
        Object obj = propObj;
        for (String path : paths) {
            String[] tmpArr = path.split("\\[");
            String propName = tmpArr[0];
            int index = tmpArr.length > 1 ? Integer.parseInt(tmpArr[1].replace("]", "")) : -1;

            String methodName = "get" + StringHelper.upperFirst(propName);
            String methodName1 = "is" + StringHelper.upperFirst(propName);
            Method getMethod = null;
            try {
                getMethod = obj.getClass().getMethod(methodName, null);
            } catch (Exception ex) {
            }

            try {
                if (getMethod == null)
                    getMethod = obj.getClass().getMethod(methodName1, null);
            } catch (Exception ex) {
            }

            obj = _getTargetValue(obj, getMethod, index, false);
            if (obj == null) {
                obj = defaultValue;
            }
        }
        return obj;
    }


    /**
     * 在某个类及其父类中查找第一个带有指定注释类的字段域。
     *
     * @param entityClass
     * @param annotationCls
     * @param <T>
     * @return
     */
    public static <T> Field findFirstFieldByAnnotation(Class<T> entityClass, Class annotationCls) {
        Field field = null;
        Class tempClass = LazyLoadParser.getValidClass(entityClass);
        while (tempClass != null) {//当父类为null的时候说明到达了最上层的父类(Object类).
            Optional optional = Arrays.stream(tempClass.getDeclaredFields())
                    .filter(x -> x.getAnnotation(annotationCls) != null).findFirst();
            if (optional.isPresent()) {
                field = (Field) optional.get();
                break;
            }
            tempClass = tempClass.getSuperclass(); //得到父类,然后赋给自己
        }
        return field;
    }

    /**
     * 在某个类及其父类中查找第一个带有指定注释类的方法。
     *
     * @param entityClass
     * @param annotationCls
     * @param <T>
     * @return
     */
    public static <T> Method findFirstMethodByAnnotation(Class<T> entityClass, Class annotationCls) {
        Method method = null;
        Class tempClass = LazyLoadParser.getValidClass(entityClass);
        while (tempClass != null) {//当父类为null的时候说明到达了最上层的父类(Object类).
            Optional optional = Arrays.stream(tempClass.getMethods())
                    .filter(x -> x.getAnnotation(annotationCls) != null).findFirst();
            if (optional.isPresent()) {
                method = (Method) optional.get();
                break;
            }
            tempClass = tempClass.getSuperclass(); //得到父类,然后赋给自己
        }
        return method;
    }

    /**
     * 在某个类及其父类中查找所有带有指定注释类的字段域。
     *
     * @param entityClass
     * @param annotationCls
     * @param depth         向父类递归的深度。0表示不向父类查找,;-1表示向父类递归查找，直到父类为NULL为止。
     * @param <T>
     * @return
     */
    public static <T> List<Field> findAllFieldByAnnotation(Class<T> entityClass, Class<? extends Annotation> annotationCls, int depth) {
        List<Field> list = new ArrayList<>();

        Class tempClass = LazyLoadParser.getValidClass(entityClass);
        while (tempClass != null) {//当父类为null的时候说明到达了最上层的父类(Object类).
            Arrays.stream(tempClass.getDeclaredFields())
                    .filter(x -> x.isAnnotationPresent(annotationCls))
                    .forEach(list::add);

            if (depth > 0)
                depth--;

            if (depth == 0)
                break;

            tempClass = tempClass.getSuperclass(); //得到父类,然后赋给自己
        }
        return list;
    }

    /**
     * 在某个类及其父类中查找所有带有指定注释类的字段域。
     *
     * @param entityClass
     * @param annotationCls
     * @param depth         向父类递归的深度。0表示不向父类查找,;-1表示向父类递归查找，直到父类为NULL为止。
     * @param <T>
     * @return
     */
    public static <T> List<Field> findAllFieldByAnnotations(Class<T> entityClass, List<Class<? extends Annotation>> annotationCls, int depth) {
        List<Field> list = new ArrayList<>();

        Class tempClass = LazyLoadParser.getValidClass(entityClass);
        while (tempClass != null) {//当父类为null的时候说明到达了最上层的父类(Object类).
            Arrays.stream(tempClass.getDeclaredFields())
                    .filter(x -> annotationCls.stream().anyMatch(y -> x.isAnnotationPresent(y)))
                    .forEach(list::add);

            if (depth > 0)
                depth--;

            if (depth == 0)
                break;

            tempClass = tempClass.getSuperclass(); //得到父类,然后赋给自己
        }
        return list;
    }

    /**
     * 在某个类及其父类中查找属性类带有指定注释类的所有字段域。
     *
     * @param entityClass
     * @param annotationCls 属性类上标注的注释类
     * @param depth         向父类递归的深度。0表示不向父类查找,;-1表示向父类递归查找，直到父类为NULL为止。
     * @param <T>
     * @return
     */
    public static <T> List<Field> findAllFieldByClassAnnotation(Class<T> entityClass, Class annotationCls, int depth) {
        List<Field> list = new ArrayList<>();

        Class tempClass = LazyLoadParser.getValidClass(entityClass);
        while (tempClass != null) {//当父类为null的时候说明到达了最上层的父类(Object类).
            Arrays.stream(tempClass.getDeclaredFields())
                    .filter(x -> ReflectHelper.getActualClassByFieldName(entityClass, x.getName()).getAnnotation(annotationCls) != null)
                    .forEach(x -> {
                        list.add((Field) x);
                    });

            if (depth == 0)
                break;

            if (depth > 0)
                depth--;

            tempClass = tempClass.getSuperclass(); //得到父类,然后赋给自己
        }
        return list;
    }

    /**
     * 获取指定类entityClass声明的(包括父类声明的)所有字段。
     *
     * @param entityClass
     * @param <T>
     * @return
     */
    public static <T> List<Field> findAllDeclaredFields(Class<T> entityClass) {
        List<Field> list = new ArrayList<>();

        Class tempClass = LazyLoadParser.getValidClass(entityClass);
        while (tempClass != null) {//当父类为null的时候说明到达了最上层的父类(Object类).
            list.addAll(Arrays.asList(tempClass.getDeclaredFields()));
            tempClass = tempClass.getSuperclass(); //得到父类,然后赋给自己
        }
        return list;
    }

    /**
     * 获取指定类entityClass声明的(包括父类声明的)第一个类型为fieldClass的字段。
     *
     * @param entityClass
     * @param fieldClass
     * @param <T>
     * @return
     */
    public static <T> Field findFirstDeclaredField(Class<T> entityClass, Class<?> fieldClass) {
        Field first = null;

        Class tempClass = LazyLoadParser.getValidClass(entityClass);
        while (tempClass != null) {//当父类为null的时候说明到达了最上层的父类(Object类).
            Optional optional = Arrays.stream(tempClass.getDeclaredFields())
                    .filter(x -> x.getType().getName().equals(entityClass.getName()))
                    .findFirst();
            if (optional.isPresent()) {
                first = (Field) optional.get();
                break;
            }

            tempClass = tempClass.getSuperclass(); //得到父类,然后赋给自己
        }

        return first;
    }


    /**
     * 在manyToOneClazz类上查找OneToManyClazz类propertyName属性映射的字段,
     *
     * @param manyToOneClazz
     * @param oneToManyClazz
     * @param propertyName
     * @return
     */
    public static Field findMappedFieldBy(Class<?> manyToOneClazz, Class<? extends Annotation> ManyToOneAnnotation,
                                          Class<?> oneToManyClazz, Class<? extends Annotation> OneToManyAnnotation, String propertyName) {
        Field first = null;

        Annotation oneToMany = null;
        try {
            Field field = ReflectHelper.findFieldByName(oneToManyClazz, propertyName);
            oneToMany = field.getAnnotation(OneToManyAnnotation);
        } catch (Exception ex) {
        }

        if (oneToMany == null)
            return first;

        String mappingName[] = new String[]{null};
        try {
            Method method = oneToMany.getClass().getMethod("mappedBy", new Class<?>[]{});
            mappingName[0] = (String) method.invoke(oneToMany, new Object[]{});
            if (mappingName[0].trim().length() <= 0)
                return first;
        } catch (Exception ex) {
        }

        if (mappingName[0] == null || mappingName[0].trim().length() <= 0)
            return first;

        List<Field> list = ReflectHelper.findAllFieldByAnnotation(manyToOneClazz, ManyToOneAnnotation, -1);
        Optional optional = list.stream().filter(x -> x.getName().equals(mappingName[0])).findFirst();
        if (optional.isPresent())
            first = (Field) optional.get();

        return first;
    }


    /**
     * 根据对象某个非集合类型字段的名称，获取该字段的值对象。
     *
     * @param obj
     * @param fieldName 非集合类型字段的名称。
     * @return
     */
    public static Object getFieldValue(Object obj, String fieldName) {
        if (obj == null) {
            return null;
        }

        try {
            Method getMethod = getMethod(obj.getClass(), fieldName);
            return getMethod.invoke(obj);
        } catch (Exception ex) {
        }

        return null;
    }

    /**
     * 根据字段名称设置对象对应字段的值。
     *
     * @param obj
     * @param fieldName 非集合类型的字段的名称
     * @param value
     * @param <T>
     * @return
     */
    public static <T> T setFieldValue(T obj, String fieldName, Object value) {
        try {
            Class clazz = ReflectHelper.getFieldClassByFieldName(obj.getClass(), fieldName);
            Object realValue = StringHelper.convertTo(value, clazz);
            Method setter = getMethod(obj.getClass(), fieldName, new String[]{"set"});
            setter.invoke(obj, realValue);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return obj;
    }

    /**
     * 获取类中对应域的 get/is 方法
     *
     * @param clazz
     * @param fieldName 非集合类型的字段的名称。
     * @return
     */
    public static Method getMethod(Class clazz, String fieldName) {
        return getMethod(clazz, fieldName, new String[]{"get", "is"});
    }


    /**
     * 获取类中对应域的指定前缀的方法对象。
     *
     * @param clazz
     * @param fieldName 非集合类型的字段的名称。
     * @param preffixes
     * @return
     */
    public static Method getMethod(Class clazz, String fieldName, String[] preffixes) {
        Method method = null;
        for (String preffix : preffixes) {
            try {
                if (preffix.toLowerCase().equals("set")) {
                    Class fieldCls = getFieldClassByFieldName(clazz, fieldName);
                    method = clazz.getMethod(preffix + StringHelper.upperFirst(fieldName), fieldCls);
                } else {
                    method = clazz.getMethod(preffix + StringHelper.upperFirst(fieldName));
                }

                if (method != null)
                    break;
            } catch (NoSuchMethodException ignored) {
            }
        }
        return method;
    }

    /**
     * 获取对象上第一个带指定注释类的字段的值，如果值为Null，则返回defaultValue。
     *
     * @param obj
     * @param clazz        注释类
     * @param defaultValue 缺省值。
     * @param <T>
     * @return
     */
    public static <T> Object getFieldValueByAnnotation(T obj, Class clazz, Object defaultValue) {
        Object idValue = null;
        Field field = ReflectHelper.findFirstFieldByAnnotation(obj.getClass(), clazz);
        if (field != null) {
            idValue = ReflectHelper.getFieldValue(obj, field.getName());
        }
        return idValue != null ? idValue : defaultValue;
    }

    /**
     * 根据字段名称获取对象上对应字段的类型
     *
     * @param clazz
     * @param fieldName 不带对象引用符"."的简单字段名称
     * @return
     */
    public static Type getFieldTypeByFieldName(Class clazz, String fieldName) {
        Field field = findFieldByName(clazz, fieldName);
        return field.getGenericType();
    }

    public static Class<?> getFieldClassByFieldName(Class clazz, String fieldName) {
        Field field = findFieldByName(clazz, fieldName);
        return field.getType();
    }

    /**
     * 查找某个域的类型
     *
     * @param clazz     类型
     * @param fieldName 域名称，可以用 .(点号) 分割表示域层级
     * @return 域的类型
     */
    public static Class<?> getFieldClassByGetterMethod(Class clazz, String fieldName) {
        Method method = getMethod(clazz, fieldName);
        return method == null ? null : method.getReturnType();
    }

    /**
     * 根据字段名称获取对象上对应字段的类型:
     * 如果字段类型为ParameterizedType类型，则返回getActualTypeArguments参数的第一个类型。
     * 如果字段类型不是ParameterizedType类型，则直接返回字段的类型。
     *
     * @param clazz
     * @param fieldName
     * @return
     */
    public static Class<?> getActualClassByFieldName(Class clazz, String fieldName) {
        Field field = findFieldByName(clazz, fieldName);
        Type gType = field.getGenericType();
        try {
            ParameterizedType pType = (ParameterizedType) gType;
            Type itemType = pType.getActualTypeArguments()[0];
            return Class.forName(itemType.getTypeName());
        } catch (Exception ex) {
        }

        return (Class) gType;
    }

    public static Class<?> getActualClassByFieldPath(Class clazz, String propertyPath, boolean readLast) {
        return getActualClassByFieldPath(clazz, propertyPath, readLast, true);
    }

    /**
     * 根据属性访问路径（例如：x.y[].z.Id）获取属性名（即点分符间隔的最后一个字符串）对应字段的类型:
     * 如果该字段类型为ParameterizedType类型，则返回getActualTypeArguments参数的第一个类型。
     * 如果该字段类型不是ParameterizedType类型，则直接返回该字段的类型。
     *
     * @param clazz
     * @param propertyPath
     * @param readLast        是否读取最后一个属性的类型，如果为false,则读取倒数第二个属性的类型。
     *                        例如"A.B.C",为真时读取的是C的类型，为假时读取的是B的类型。
     * @param readActualClass 确定最后一次读取类型时，是否读取的是ActualClass。
     * @return
     */
    public static Class<?> getActualClassByFieldPath(Class clazz, String propertyPath, boolean readLast, boolean readActualClass) {
        try {
            propertyPath = propertyPath.replaceAll("\\[\\]", "");
            String[] fieldNames = StringHelper.splitWithoutEmpty(propertyPath, "\\.");
            clazz = LazyLoadParser.getValidClass(clazz);
            final Class[] tmpClazz = {clazz};
            int loopCount = fieldNames.length - (readLast ? 0 : 1);
            for (int i = 0; i < loopCount; i++) {
                //判断是否是最后一次读取。
                if (i + 1 >= loopCount && readLast) {
                    tmpClazz[0] = readActualClass ? getActualClassByFieldName(tmpClazz[0], fieldNames[i])
                            : getFieldClassByFieldName(tmpClazz[0], fieldNames[i]);
                    continue;
                }
                tmpClazz[0] = getActualClassByFieldName(tmpClazz[0], fieldNames[i]);
            }
            return tmpClazz[0];
        } catch (Exception ex) {
        }
        return null;
    }

    /**
     * 在类上查找指定字段名称的字段对象。
     *
     * @param clazz
     * @param fieldName 不带对象引用符"."的简单字段名称
     * @param <T>
     * @return
     */
    public static <T> Field findFieldByName(Class clazz, String fieldName) {
        Field field = null;
        try {
            Class tempClass = LazyLoadParser.getValidClass(clazz);
            while (tempClass != null) {//当父类为null的时候说明到达了最上层的父类(Object类).
                //根据字段进行匹配。
                Optional optional = Arrays.stream(tempClass.getDeclaredFields())
                        .filter(x -> x.getName().equals(fieldName)).findFirst();
                if (optional.isPresent()) {
                    field = (Field) optional.get();
                    break;
                }
                tempClass = tempClass.getSuperclass(); //得到父类,然后赋给自己
            }
        } catch (Exception ex) {
        }
        return field;
    }

    public static <T> void clearProperties(T data, List<String> properties) {
        try {
            for (String s : properties) {
                if ("".equals(s.trim()))
                    continue;

                String[] propertyArray = s.split("\\.");
                Object propValue = data;
                for (int i = 0; i < propertyArray.length - 1; i++) {
                    if (propValue == null)
                        break;

                    if (!"".equals(propertyArray[i].trim())) {
                        propValue = ReflectHelper.getFieldValue(propValue, propertyArray[i].trim());
                    }
                }

                if (propValue != null && !"".equals(propertyArray[propertyArray.length - 1])) {
                    setFieldValue(propValue, propertyArray[propertyArray.length - 1], null);
                }
            }
        } catch (Exception ex) {
            ex.printStackTrace();
        }
    }

    /**
     * 获取Class类的所有非静态字段名 <br>
     *
     * @param clazz
     * @return
     */
    public static Collection<String> getNotStaticClassFieldNameCollection(Class<?> clazz) {
        if (clazz == null) {
            throw new NullPointerException("传入的clazz为空对象！");
        }
        Field[] fields = clazz.getDeclaredFields();
        Collection<String> fieldNames = new ArrayList<>();
        Arrays.stream(fields).forEach(x -> {
            if (!Modifier.isStatic(x.getModifiers())) {
                fieldNames.add(x.getName());
            }
        });
        return fieldNames;
    }

    /**
     * 获取某个类指定名称的公共、静态、不可变字段的值。
     *
     * @param clazz
     * @param fieldName
     * @return
     */
    public static Object getPublicStaticFinalFieldValue(Class<?> clazz, String fieldName) {
        if (clazz == null) {
            throw new NullPointerException("传入的clazz为空对象！");
        }

        if (fieldName == null || fieldName.trim().equals("")) {
            throw new NullPointerException("传入的fieldName为空对象！");
        }

        Field[] fields = clazz.getDeclaredFields();
        Optional<Field> optional = Arrays.stream(fields).filter(x -> Objects.equals(x.getName(), fieldName)).findFirst();
        if (optional.isPresent()) {
            Field field = optional.get();
            if (Modifier.isStatic(field.getModifiers())
                    && Modifier.isPublic(field.getModifiers())
                    && Modifier.isFinal(field.getModifiers())) {
                try {
                    return field.get(clazz);
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                }
            }
        }
        return null;
    }

    /**
     * 获取某个类指定名称的公共、静态、不可变字段对象。
     *
     * @param clazz
     * @param fieldAnnotationClazz
     * @param <T>
     * @return
     */
    public static <T extends Annotation> List<Field> getPublicStaticFinalField(Class<?> clazz, Class<T> fieldAnnotationClazz) {
        if (clazz == null) {
            throw new NullPointerException("传入的clazz为空对象！");
        }

        if (fieldAnnotationClazz == null) {
            throw new NullPointerException("传入的fieldAnnotationClazz为空对象！");
        }

        List<Field> list = new ArrayList<>();

        Field[] fields = clazz.getDeclaredFields();
        Arrays.stream(fields)
                .filter(x -> x.getAnnotation(fieldAnnotationClazz) != null)
                .forEach(x -> {
                    if (Modifier.isStatic(x.getModifiers())
                            && Modifier.isPublic(x.getModifiers())
                            && Modifier.isFinal(x.getModifiers())) {
                        list.add(x);
                    }
                });
        return list;
    }

    /**
     * 同步两个对象的同名属性。
     *
     * @param sourceObj
     * @param targetObj
     */
    public static void syncSameNameProperties(Object sourceObj, Object targetObj) {
        String[] prefixArray = new String[]{"get", "is"};

        List<Method> targetMethods = Arrays.stream(targetObj.getClass().getMethods())
                .filter(x -> x.getName().startsWith("set"))
                .collect(Collectors.toList());
        if (targetMethods.isEmpty())
            return;

        List<Method> sourceMethods = Arrays.stream(sourceObj.getClass().getMethods())
                .filter(x -> Arrays.stream(prefixArray).anyMatch(y -> x.getName().startsWith(y)))
                .collect(Collectors.toList());
        if (sourceMethods.isEmpty())
            return;

        targetMethods.forEach(x -> {
            String propertyName = x.getName().replaceFirst("set", "");
            Method srcMethod = sourceMethods.stream().filter(y -> y.getName().endsWith(propertyName)).findFirst().orElse(null);
            if (srcMethod == null)
                return;

            try {
                Object value = srcMethod.invoke(sourceObj, null);
                x.invoke(targetObj, value);
            } catch (Exception ex) {
            }
        });
    }

    private static Object _getTargetValue(Object obj, Method getMethod, int index, boolean autoCreateDefaultValue) {
        try {
            obj = getMethod.invoke(obj, null);
            if (obj == null) {
                obj = autoCreateDefaultValue ? getMethod.getReturnType().newInstance() : null;
            }

            if (index >= 0 && obj != null) {
                List list = (List) obj;
                if (index >= list.size()) {
                    //填充缺失的列表项
                    for (int i = list.size(); i <= index; i++) {
                        list.add(getMethod.getReturnType().newInstance());
                    }
                }
                obj = list.get(index);
            }
        } catch (Exception ex) {
        }
        return obj;
    }
}
